package postmortem;

import java.awt.*;
import java.awt.event.*;
import java.awt.geom.AffineTransform;

import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreeModel;


import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;

public class MyTool {

	public static void main(String[] args) throws IOException {
		final PaintWindow window = new PaintWindow();
		window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		window.setVisible(true);
		System.out.println("Java UI-Inspector started");
		window.setExtendedState(JFrame.MAXIMIZED_BOTH);
		window.setResizable(false);

	}

}

class PaintWindow extends JFrame implements MouseListener {

	public static PaintPanel canvas = new PaintPanel();
	JPanel panel2 = new JPanel();
	JPanel panel3 = new JPanel();
	public static JSlider jSliderOne;
	public static Thread current = new SliderThread(jSliderOne);
	public boolean paused = false;
	JScrollPane scrollPane = new JScrollPane(canvas);
	public static ArrayList<MethodParam> allMethods;
	public static JTextArea txt;
	public static LinkedList<Integer> sliderValues = new LinkedList<Integer>();

	public static BufferedWriter writer;

	public PaintWindow() throws IOException {

		try {

			writer = new BufferedWriter(new FileWriter("log.txt"));
		} catch (IOException e) {
		}

		allMethods = new ArrayList<MethodParam>();
		completeMethods();

		Container content = this.getContentPane();
		content.setLayout(new BorderLayout());
		// this.setPreferredSize(new Dimension(800, 600));

		// content.add(canvas, BorderLayout.NORTH);
		// scrollPane.add(canvas);
		// JSplitPane vpane = new JSplitPane(JSplitPane.VERTICAL_SPLIT,
		// scrollPane, panel2);
		// vpane.setResizeWeight(0.0);

		// scrollPane.setSize(new Dimension(800, 600));
		final JButton btnStart = new JButton("Play");
		btnStart.setPreferredSize(new Dimension(90, 30));
		final JButton btnPause = new JButton("Pause");
		btnPause.setPreferredSize(new Dimension(90, 30));
		final JButton btnStop = new JButton("Stop");
		btnStop.setPreferredSize(new Dimension(90, 30));
		scrollPane.setPreferredSize(new Dimension(800, 650));
		panel2.setSize(new Dimension(800, 100));

		txt = new JTextArea();
		txt.setText("Code Source Line: ");

		txt.setEditable(false);
		// txt.setPreferredSize(new Dimension(200, 60));

		JScrollPane txtScroll = new JScrollPane(txt);
		txtScroll.setPreferredSize(new Dimension(200, 60));
		jSliderOne = new JSlider(0, allMethods.size());
		jSliderOne.setPreferredSize(new Dimension(350, 30));
		jSliderOne.setValue(0);
		jSliderOne.setPaintLabels(true);
		jSliderOne.addChangeListener(new BoundedChangeListener());

		ActionListener play = new ActionListener() {
			public void actionPerformed(ActionEvent actionEvent) {
				if (current.isAlive()) {
					if (paused) {
						current.resume();
						paused = false;
						btnPause.setText("Pause");
					}
				} else {
					jSliderOne.setValue(1);
					jSliderOne.repaint();
					current = new SliderThread(jSliderOne);
					current.start();
				}
			}
		};

		ActionListener pause = new ActionListener() {
			public void actionPerformed(ActionEvent actionEvent) {
				if (current.isAlive())
					if (paused) {
						current.resume();
						paused = false;
						btnPause.setText("Pause");
					} else {

						current.suspend();
						btnPause.setText("Resume");
						paused = true;
					}
			}
		};

		ActionListener stop = new ActionListener() {
			public void actionPerformed(ActionEvent actionEvent) {
				if (current.isAlive()) {
					current.stop();
					jSliderOne.setValue(0);
					jSliderOne.repaint();
					paused = false;
					btnPause.setText("Pause");
				}
			}
		};

		btnStart.addActionListener(play);
		btnStart.addMouseListener(this);
		btnPause.addActionListener(pause);
		btnPause.addMouseListener(this);
		btnStop.addActionListener(stop);
		btnStop.addMouseListener(this);
		panel2.add(txtScroll);
		panel2.add(btnStart, BorderLayout.NORTH);
		panel2.add(btnPause, BorderLayout.NORTH);
		panel2.add(jSliderOne, BorderLayout.NORTH);
		panel2.add(btnStop, BorderLayout.NORTH);
		content.add(panel3, BorderLayout.NORTH);
		content.add(scrollPane, BorderLayout.NORTH);

		content.add(panel2, BorderLayout.SOUTH);

		this.setTitle("Java UI-Inspector");
		this.pack();
		JTree tree = new JTree(postmortem.Tracing.root);
		// System.out.println("Total records in the HashMap: "
		// + tracing.Tracing.prova.size());
		// System.out.println("Total methods: "
		// + tracing.Tracing.calledMethods.size());

		// JScrollPane scroll = new JScrollPane(tree);

		// scroll.setPreferredSize(new Dimension(300, 600));

		// content.add(scroll, BorderLayout.WEST);

		// JRootPane jr = (JRootPane) traverse(tree);

	}

	public void completeMethods() {
		
		int provaSize = postmortem.Tracing.prova.size();
		int prova2Size = postmortem.Tracing.prova2.size();
		int hash = 0;
		int listSize = 0;
		
		for (int i = 0; i < provaSize; i++) {
			hash = postmortem.Tracing.prova.get(i);
			ArrayList<MethodParam> list = postmortem.Tracing.objectMethods
					.get(hash);
			listSize = list.size();
			MethodParam m;
			for (int j = 0; j < listSize; j++) {
				allMethods.add(list.get(j));
				m = list.get(j);
				try {
					writer.write("\n" + m.command.toString() + " "
							+ m.sourceLocation + " " + m.object);
				} catch (IOException e) {

					e.printStackTrace();
				}
			}

		}
		/**
		if (prova2Size < provaSize) {
			for (int i = 0; i < prova2Size; i++) {
				hash = tracing.Tracing.prova2.get(i);
				ArrayList<MethodParam> list = tracing.Tracing.objectMethods
						.get(hash);
				listSize = list.size();
				MethodParam m;
				for (int j = 0; j < listSize; j++) {
					//allMethods.add(list.get(j));
					m = list.get(j);
					try {
						writer.write("\n" + m.command.toString() + " "
								+ m.sourceLocation + " " + m.object);
					} catch (IOException e) {

						e.printStackTrace();
					}
				}

			}
		} 

		try {
			writer.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} */
	}

	public void addTo(Container to, Container from) {
		Component[] allComp = from.getComponents();
		JScrollPane myP = new JScrollPane();
		for (Component cmp : allComp) {
			if (cmp instanceof JScrollPane)
				myP.setLocation(cmp.getLocation());
			to.add(myP);
			// System.out.println("Panel added: "
			// + cmp.getClass().getCanonicalName());
			addTo(to, (Container) cmp);
		}
	}

	public Object traverse(JTree tree) {
		TreeModel model = tree.getModel();
		if (model != null) {
			DefaultMutableTreeNode obj = (DefaultMutableTreeNode) model
					.getRoot();
			Object root = model.getRoot();
			// System.out.println("Root: " + root.toString());
			walk(model, root);
			return obj.getUserObject();
		} else
			// System.out.println("Tree is empty.");
			return null;
	}

	protected void walk(TreeModel model, Object o) {
		int cc;
		cc = model.getChildCount(o);
		for (int i = 0; i < cc; i++) {
			Object child = model.getChild(o, i);
			if (model.isLeaf(child)) {
			}
			// System.out.println("Child: " + child.toString());
			else {
				// System.out.println(child.toString() + "----------");
				walk(model, child);
			}
		}
	}

	@Override
	public void mouseClicked(MouseEvent arg0) {

	}

	public void mouseEntered(MouseEvent arg0) {
	}

	@Override
	public void mouseExited(MouseEvent arg0) {
	}

	@Override
	public void mousePressed(MouseEvent arg0) {
	}

	@Override
	public void mouseReleased(MouseEvent arg0) {
	}
}

class PaintPanel extends JPanel implements MouseListener, MouseMotionListener {

	static BufferedImage bufferedImage = null;
	private BufferedImage bfPointer = null;
	public static HashMap<Integer, Method> methods = new HashMap<Integer, Method>();
	static HashMap<Integer, String[]> pixelMap;
	public static LinkedList<Image> imageList = new LinkedList<Image>();
	LinkedList<Integer> index;
	LinkedList<String> fullCommands;
	LinkedList<String[]> command = new LinkedList<String[]>();
	LinkedList<String[]> params;
	public static final int width = postmortem.Tracing.mainFrame.getWidth();
	public static final int height = postmortem.Tracing.mainFrame.getHeight();
	public static int[] pixelColor = new int[width * height];
	public static int[] pixelColor2 = new int[width * height];
	public static LinkedList<String[]> sourceList = new LinkedList<String[]>();
	public static LinkedList<String> colorList = new LinkedList<String>();
	static int from;
	static int to;
	static int counter_from;
	static int counter_to;
	static int[] initPixels;
	static int[] pixels;
	static int startX = 0;
	static int startY = 0;
	static int endX = 0;
	static int endY = 0;

	static int startX2 = 0;
	static int startY2 = 0;
	static int endX2 = 0;
	static int endY2 = 0;

	static boolean check = true;
	static int total;
	String results[];
	static int start = 1;
	static int stop = 1;

	public PaintPanel() {
		setPreferredSize(new Dimension(width, height));
		setBackground(Color.white);
		pixelMap = new HashMap<Integer, String[]>();
		this.addMouseListener(this);
		this.addMouseMotionListener(this);
		fullCommands = new LinkedList<String>();
		index = new LinkedList<Integer>();
		start = 1;
		stop = 1;
		// Read all the Graphics methods and put them to a hashmap
		Method[] storedMethods = java.awt.Graphics2D.class.getMethods();
		int length = storedMethods.length;
		// results = test.split(":\\s*");
		for (int i = 0; i < length; i++) {
			int hash = storedMethods[i].toString().hashCode();
			methods.put(hash, storedMethods[i]);
		}
		Method mee = methods.get("fillOval");
		// System.out.println("MethodHash:" + mee.hashCode());
		counter_from = 0;
		counter_to = 1;
		total = 0;

	}

	public void paintComponent(Graphics g) {
		super.paintComponent(g);

		Graphics2D g2 = (Graphics2D) g;

		if (bufferedImage == null) {

			int w = width;
			int h = height;
			if (w <= 0)
				w = 600;
			if (h <= 0)
				h = 600;

			bufferedImage = (BufferedImage) this.createImage(w, h);
			Graphics2D gc = bufferedImage.createGraphics();

			gc.setColor(Color.white);
			gc.fillRect(0, 0, w, h);
			// System.out.println("Width: " +
			// tracing.Tracing.mainFrame.getWidth()
			// + "\tHeight: " + tracing.Tracing.mainFrame.getHeight());
			bufferedImage.getRGB(0, 0, width, height, pixelColor, 0, width);
			bufferedImage.getRGB(0, 0, width, height, pixelColor2, 0, width);
			String[] s = new String[2];
			s[0] = "none";
			s[1] = "none";
			for (int i = 0; i < width; i++) {
				for (int j = 0; j < height; j++) {
					pixelMap.put(("" + i + j).hashCode(), s);
				}
			}
		}

		// g2.drawImage(tracing.Tracing.testimg, null, 0, 0);
		g2.drawImage(bufferedImage, null, 0, 0);
		// bufferedImage.getRGB(0, 0, width, height, pixelColor, 0, width);

		// g2.drawImage(imageList.peekLast(), 0, 0, this); // draw previous
		// shapes
	}

	public void drawPointer(Graphics2D g) {
		if ((startX - 1) > 0 && (startY - 1) > 0) {
			if (((endX2 - startX2) + 2) < bufferedImage.getWidth()
					&& ((endY2 - startY2) + 2) < bufferedImage.getHeight()) {
				g.setColor(Color.red);
				if (pixelColor.length != 0) {

					bufferedImage.setRGB(startX2, startY2,
							(endX2 - startX2) + 2, (endY2 - startY2) + 2,
							pixelColor, 0, (endX2 - startX2) + 10);
				}

				bufferedImage.getRGB((startX - 1), (startY - 1),
						(endX - startX) + 2, (endY - startY) + 2, pixelColor,
						0, (endX - startX) + 10);
				startX2 = startX - 1;
				endX2 = endX - 1;
				startY2 = startY - 1;
				endY2 = endY - 1;
				g.setStroke(new BasicStroke(2.0f));
				g.drawOval(startX, startY, (endX - startX), (endY - startY));

				sourceList.clear();
				colorList.clear();

				for (int i = startX; i <= endX; i++) {
					for (int j = startY; j <= endY; j++) {
						int key = ("" + i + j).hashCode();

						if (!sourceList.contains(pixelMap.get(key))) {
							sourceList.add(pixelMap.get(key));
						}
					}
				}

				PaintWindow.txt.setText("Code Source Line:");
				for (String[] s : sourceList) {
					PaintWindow.txt.append("\nCode:" + s[0]);
					PaintWindow.txt.append("\nColor: " + s[1]);
				}
			}
		}
		System.out.println("Out of bounds");

	}

	public void mouseReleased(MouseEvent e) {

		// this.nextPaint();

		endX = e.getX();
		endY = e.getY();

		Graphics2D graphicPointer = bufferedImage.createGraphics();
		drawPointer(graphicPointer);
		this.repaint();
	}

	// unimplemented methods
	public void mouseDragged(MouseEvent e) {
		endX = e.getX();
		endY = e.getY();
		// this.repaint(); // TODO FIX THIS
	}

	public void mouseMoved(MouseEvent e) {
	}

	public void mouseClicked(MouseEvent e) {
	}

	public void mouseEntered(MouseEvent e) {
	}

	public void mouseExited(MouseEvent e) {
	}

	public void mousePressed(MouseEvent e) {
		startX = e.getX();
		startY = e.getY();
		endX = startX;
		endY = startY;

	}
}

class BoundedChangeListener implements ChangeListener {

	public void stateChanged(ChangeEvent changeEvent) {
		Object source = changeEvent.getSource();
		if (source instanceof JSlider) {
			JSlider theJSlider = (JSlider) source;

			if (!theJSlider.getValueIsAdjusting()) {
				int oldValue = 1;
				if (PaintWindow.sliderValues.size() > 0)
					oldValue = PaintWindow.sliderValues.poll();
				PaintWindow.sliderValues.add(theJSlider.getValue());

				try {
					if (oldValue < theJSlider.getValue()) {
						this.nextPaint(oldValue, theJSlider.getValue());
					} else {
						this.nextPaint(1, theJSlider.getValue());
					}

				} catch (IllegalArgumentException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (InvocationTargetException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		} else {
			// System.out.println("Something changed: " + source);
		}
	}

	public void nextPaint(int i, int j) throws IllegalArgumentException,
			IllegalAccessException, InvocationTargetException {
		// --- Draw the current shape onto the buffered image.
		// System.out.println("Start: " + PaintPanel.start + " Stop: "
		// + PaintPanel.stop);
		Graphics2D grafarea = PaintPanel.bufferedImage.createGraphics();

		PaintPanel.start = 1;
		PaintPanel.stop = postmortem.Tracing.prova.size();
		drawCurrentShape(grafarea, i, j);

	}

	public void updatePixelMap(Shape shape, Color color, String sourceLoc,
			String colorLoc) {
		PaintWindow.canvas.repaint();

		int x1 = shape.getBounds().x;
		int y1 = shape.getBounds().y;
		int x2 = (int) shape.getBounds().getMaxX();
		int y2 = (int) shape.getBounds().getMaxY();

		int offset = 0;
		int startX = 0;
		int startY = 0;
		int scansize = PaintPanel.width;

		int pixelKey = 0;
		String[] s = new String[2];
		s[0] = sourceLoc;
		s[1] = colorLoc;
		for (int i = x1; i <= x2; i++) {
			for (int j = y1; j <= y2; j++) {
				pixelKey = ("" + i + j).hashCode();
				// currentPixel = PaintPanel.pixelColor[offset +
				// (i-startY)*scansize + (j-startX)];
				// if (currentPixel == oldPixel)
				PaintPanel.pixelMap.put(pixelKey, s);
			}
		}
		PaintPanel.pixelColor2 = PaintPanel.pixelColor.clone();
	}

	private void drawCurrentShape(Graphics g, int st, int end)
			throws IllegalArgumentException, IllegalAccessException,
			InvocationTargetException {
		Graphics2D g2 = (Graphics2D) g;
		/**
		 * for (int j = st; j <= end; j++) {
		 * System.out.println("Painting component: " + j);
		 * 
		 * int current = tracing.Tracing.prova.get(j); ArrayList<MethodParam>
		 * list = tracing.Tracing.objectMethods .get(current); int size =
		 * list.size();
		 * 
		 * if (list.size() > 0) { Integer[] pC = list.get(0).publicComp; Color
		 * color = new Color(pC[4]); g2.setColor(color); g2.fillRect(pC[0],
		 * pC[1], pC[2], pC[3]); }
		 */

		for (int i = st; i <= end; i++) {

			MethodParam currentEntry = PaintWindow.allMethods.get(i);
			// System.out.println("Command: " + currentEntry.command);
			Method m = PaintPanel.methods.get(currentEntry.methodHash);
			Graphics2D currentG = (Graphics2D) currentEntry.gr;
			AffineTransform original = g2.getTransform();
			Rectangle originalClip = g2.getClipBounds();
			// g2.scale(0.8, 0.8);
			
			// g2.setTransform(currentG.getTransform());
			//g2.setClip(currentEntry.publicComp.getBounds());
			
			g2.setBackground(currentG.getBackground());
			g2.setColor(currentEntry.color);
			
			g2.setStroke(currentEntry.stroke);
			g2.setFont(currentG.getFont());
			

			if (currentEntry.object[0] == null) {
				m.invoke(g2);
			} else {

				/**
				 * if (currentEntry.command.equals("drawImage")) { int dx1 =
				 * (Integer) currentEntry.object[1]; int dy1 = (Integer)
				 * currentEntry.object[2]; int dx2 = (Integer)
				 * currentEntry.object[3]; int dy2 = (Integer)
				 * currentEntry.object[4]; int sx1 = (Integer)
				 * currentEntry.object[5]; int sy1 = (Integer)
				 * currentEntry.object[6]; int sx2 = (Integer)
				 * currentEntry.object[7]; int sy2 = (Integer)
				 * currentEntry.object[8]; g2.drawImage((Image)
				 * currentEntry.object[0], dx1, dy1, sx2, sy2,
				 * PaintWindow.canvas);
				 * 
				 * } else { if (currentEntry.command.equals("drawString")) {
				 * String txt = currentEntry.object[0].toString(); int x =
				 * (Integer) currentEntry.object[1]; int y = (Integer)
				 * currentEntry.object[2]; g2.drawString(txt, x, y); } else
				 * 
				 * m.invoke(g2, currentEntry.object); }
				 */
				m.invoke(g2, currentEntry.object);
			}
			
			updatePixelMap(currentEntry.shape, g2.getColor(),
					currentEntry.sourceLocation, currentEntry.colorLoc); // check
																			// here
			g2.setTransform(original);
			g2.setClip(originalClip);
		}

		g2.dispose();

	}
}

class SliderThread extends Thread {
	JSlider slider;

	public SliderThread(JSlider sl) {
		this.slider = sl;
	}

	@Override
	public void run() {

		Runnable runner = new Runnable() {
			public void run() {
				int value = slider.getValue();
				slider.setValue(value + 1);
			}
		};

		for (int i = 1; i <= slider.getMaximum(); i++) {
			try {
				SwingUtilities.invokeAndWait(runner);
				// Thread.sleep(50);
			} catch (InterruptedException ignoredException) {
			} catch (InvocationTargetException ignoredException) {
			}
		}
	}

}



